package nape.shape;
import nape.shape.Circle;
import nape.shape.Polygon;
import nape.geom.AABB;
import nape.geom.Axis;
import nape.geom.Mat23;
import nape.geom.Vec2;
import nape.geom.VecMath;
import nape.util.FastMath;
import nape.util.MixPool;
import nape.util.Names;
import nape.util.NpAlgorithm;
import nape.util.NpList;
import nape.phys.Material;
import nape.Config;
import nape.Const;
import nape.util.NpNode_ZNContact;
import nape.util.NpAllocList_ZNContact;
import nape.util.NpNode_ZNV2;
import nape.util.NpList_ZNV2;
import nape.util.NpNode_Vec2__Dynamic;
import nape.util.NpList_Vec2__Dynamic;
import nape.util.NpNode_ZNShp;
import nape.util.NpList_ZNShp;

//'newfile' generated imports
import nape.util.NpNode_ZNShp;
import nape.util.NpList_ZNShp;














    class ShapeIterator {
        
        
    static public var pool:ShapeIterator;
    public var pool_next:ShapeIterator;

    
        public var cur:NpNode_ZNShp;
    
        public function new() {}
        public static inline function get(list:NpList_ZNShp) {
            var ret =  {
    if(ShapeIterator.pool==null) new ShapeIterator();
    else {
        var ret = ShapeIterator.pool;
        ShapeIterator.pool = ret.pool_next;
        ret;
    }
};
            ret.cur = list.begin();
            return ret;
        }
        public inline function free() {
            cur = null;
        }
        
        public inline function hasNext():Bool {
            if(cur==null) {
                 {
    this.free();
    this.pool_next = ShapeIterator.pool;
    ShapeIterator.pool = this;
};
                return false;
            }else
                return true;
        }
        public inline function next():Shape {
            
            
            var _ret = cur.elem();
            cur = cur.next;
            return  (_ret.outer_shape);
        }
        
    }





class Shape {
    public var znp_inner_shape:ZNShp;
    
    
    
    
    public static inline var CIRCLE :Int = 0;
    public static inline var POLYGON:Int = 1;
    
    
    public var type(get_type,never):Int;
    public inline function get_type() return znp_inner_shape.type
    
    
     
    public var material(get_material,set_material):Material;
    
    public inline function get_material() return znp_inner_shape.material.outer
    
    public inline function set_material(material:Material) {
        if(material==null) material = Config.DEFAULT_MATERIAL;
        znp_inner_shape.setMaterial(material.znp_inner);
        
        return this.material;
    }
    
    
    
    public var id(get_id,never):Int;
    public inline function get_id() return znp_inner_shape.id
    
    

    public var circle(get_circle,never):Circle;
    public inline function get_circle() {
        return if(isCircle) znp_inner_shape.circle.outer
        else null;
    }
    
    public var polygon(get_polygon,never):Polygon;
    public inline function get_polygon() {
        return if(isPolygon) znp_inner_shape.polygon.outer
        else null;
    }
    
    
    
    public var isCircle (get_isCircle, never):Bool;
    public var isPolygon(get_isPolygon,never):Bool;
    public inline function get_isCircle () return type==Shape.CIRCLE
    public inline function get_isPolygon() return type==Shape.POLYGON
    
    
    
    public var worldCOM(get_worldCOM,never):Vec2;
    
    public inline function get_worldCOM() {
        if(znp_inner_shape.wrap_worldCOM==null)
            znp_inner_shape.wrap_worldCOM = new Vec2(0,0);
        
        znp_inner_shape.validate_worldCOM();
        return znp_inner_shape.wrap_worldCOM;
    }
    
    
    
    public var localCOM(get_localCOM,set_localCOM):Vec2;
    
    public inline function get_localCOM() {
        if(znp_inner_shape.wrap_localCOM==null) {
            znp_inner_shape.wrap_localCOM = new Vec2(0,0);
                { znp_inner_shape.wrap_localCOM.znp_inner.x = znp_inner_shape.localCOMx; znp_inner_shape.wrap_localCOM.znp_inner.y = znp_inner_shape.localCOMy; };
            if(isCircle)
                znp_inner_shape.wrap_localCOM.znp_broadcast.add(circle.znp_inner.setLocalCOM);
        }
            
        znp_inner_shape.validate_localCOM();
        return znp_inner_shape.wrap_localCOM;
    }
    
    public inline function set_localCOM(localCOM:Vec2) {
        if(isCircle) {
            if(znp_inner_shape.wrap_localCOM!=null)
                znp_inner_shape.wrap_localCOM.znp_broadcast.remove(znp_inner_shape.circle.setLocalCOM);
                
            znp_inner_shape.wrap_localCOM = localCOM;
            localCOM.znp_broadcast.add(znp_inner_shape.circle.setLocalCOM);

            znp_inner_shape.invalidate_localCOM();
        }else {}
        return this.localCOM;
    }
    
    
    
    public var area       (get_area,       never):Float;
    public var inertia    (get_inertia,    never):Float;
    public var angDragCoef(get_angDragCoef,never):Float;
    
    public inline function get_area() {
        znp_inner_shape.validate_area_inertia();
        return znp_inner_shape.area;
    }
    public inline function get_inertia() {
        znp_inner_shape.validate_area_inertia();
        return znp_inner_shape.inertia;
    }
    public inline function get_angDragCoef() {
        znp_inner_shape.validate_angDrag();
        return znp_inner_shape.angDrag;
    }
    
    
    
    public inline function translate(displacement:Vec2) {
        if(displacement.lsq>0) {
            if(isCircle) znp_inner_shape.circle.__translate(displacement.x,displacement.y);
            else         znp_inner_shape.polygon.__translate(displacement.x,displacement.y);
        }
    }

    public inline function scale(scale:Float) {
        if(scale!=1.0) {
            if(isCircle) znp_inner_shape.circle.__scale(scale);
            else         znp_inner_shape.polygon.__scale(scale,scale);
        }
    }
    
    public inline function scale2d(scaleX:Float,scaleY:Float) {
        if((scaleX*scaleX*scaleY*scaleY>Const.EPSILON) && (scaleX!=1.0 || scaleY!=1.0)) {
            if(isCircle) {
                var d = scaleX-scaleY;
                if(d*d<Const.EPSILON)
                    znp_inner_shape.circle.__scale((scaleX+scaleY)*0.5);
            }
            else znp_inner_shape.polygon.__scale(scaleX,scaleY);
        }
    }
    
    public inline function rotate(angle:Float) {
        var dr = angle%(2*Math.PI);
        if(dr!=0) {
            var cos = Math.cos(angle);
            var sin = Math.sin(angle);
            if(isCircle) znp_inner_shape.circle.__rotate(sin,cos);
            else         znp_inner_shape.polygon.__rotate(sin,cos);
        }
    }
    
    public inline function transform(matrix:Mat23) {
        var det =  (matrix.a*matrix.d - matrix.b*matrix.c);
        if(det*det>Const.EPSILON) {
            if(isCircle && matrix.eqorthogonal)
                znp_inner_shape.circle.__transform(matrix);
            else
                znp_inner_shape.polygon.__transform(matrix);
        }
    }

    
    
    
    public function toString() return ""
}



class ZNShp {
    public var outer_shape:Shape;
    
    
    
    public var type:Int;
    public var id:Int;
    static var nextId:Int=0;
    
    
    
    public var circle :ZNCirc;
    public var polygon:ZNPoly;
    
    
    
    public var area   :Float; public var  _inv_area_inertia:Bool;
    public var inertia:Float; 
    public var angDrag:Float; public var  _inv_angDrag:Bool;
    
    
    
     public var localCOMx:Float; public var localCOMy:Float; public var  _inv_localCOM:Bool;
     public var worldCOMx:Float; public var worldCOMy:Float; public var  _inv_worldCOM:Bool;
    
    public var wrap_localCOM:Vec2; 
    public var wrap_worldCOM:Vec2; 
    
    
    
    public var material:ZNMat;
    
    
    
    public inline function validate_area_inertia() {
        if( _inv_area_inertia) {
             _inv_area_inertia = false;
            if(type==Shape.CIRCLE) circle.__validate_area_inertia();
            else                   polygon.__validate_area_inertia();
        }
    }
    public inline function validate_angDrag() {
        if( _inv_angDrag) {
             _inv_angDrag = false;
            if(type==Shape.CIRCLE) circle.__validate_angDrag();
            else                   polygon.__validate_angDrag();
        }
    }
    public inline function validate_localCOM() {
        if( _inv_localCOM) {
            if(type==Shape.POLYGON)
                polygon.__validate_localCOM(); 
            
             _inv_localCOM = false;
            if(wrap_localCOM!=null)
                    { wrap_localCOM.znp_inner.x = localCOMx; wrap_localCOM.znp_inner.y = localCOMy; };
        }
    }
    public inline function validate_worldCOM() {
        if( _inv_worldCOM) {
             _inv_worldCOM = false;
            
            if(wrap_worldCOM!=null)
                    { wrap_worldCOM.znp_inner.x = worldCOMx; wrap_worldCOM.znp_inner.y = worldCOMy; };
        }
    }
    
    
    
    public inline function invalidate_angDrag() {
         _inv_angDrag = true;
    }
    public inline function invalidate_area_inertia() {
         _inv_area_inertia = true;
    }
    public inline function invalidate_localCOM() {
         _inv_localCOM = true;
        invalidate_area_inertia();
        invalidate_angDrag();
        invalidate_worldCOM();
    }
    public inline function invalidate_worldCOM() {
         _inv_worldCOM = true;
    }
    
    
    public inline function invalidate_material(flags:Int) {
        if((flags&ZNMat.WAKE)!=0) {}
        if((flags&ZNMat.PROPS)!=0) {}
        
        if((flags&ZNMat.ANGDRAG)!=0)
            invalidate_angDrag();
    }
    
    
    
    
    public function new(type:Int, material:ZNMat) {
        this.type = type;
        setMaterial(material);
        id = nextId++;

         _inv_area_inertia =  _inv_angDrag =  _inv_localCOM = true;
    }
    
    
    
    
    public inline function setMaterial(material:ZNMat) {
        if(this.material!=material) {
            if(this.material!=null)
                this.material.remShape(this);
        
            this.material = material;
            material.addShape(this);
        }
    }
}
